/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.cef.pui.pob;

import cef.pui.pob.Builder;
import cef.pui.pob.BuilderException;
import commons.provider.ProviderException;
import commons.provider.ProviderManager;
import commons.util.ValidationUtil;
import genomemap.data.OrganismData;
import genomemap.provider.Providers.OrganismDataProvider;

import javax.xml.bind.PropertyException;
import java.io.File;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;

/**
 * @author Susanta Tewari
 * @since Jun 16, 2011
 */
public final class OrganismDataBuilder extends Builder<OrganismData> {

    /** Field description */
    private String PROP_URL = "url";

    /** Field description */
    private String PROP_LINKAGE_GROUPS = "linkage-groups";

    /** Field description */
    private OrganismData data;

    /**
     * Builds an instance of {@code OrganismData} using the following properties in {@code props}.
     * They are <ul> <li>{@link OrganismDataBuilder#PROP_URL} </li> <li>{@link
     * OrganismDataBuilder#PROP_LINKAGE_GROUPS}</li> </ul> Their valid values are described in
     * {@link OrganismDataProvider}.
     *
     * @param propertyKeyPrefix property key prefix
     * @param props {@code Properties} object containing the required properties
     * @throws BuilderException If any error occurs building the value object
     */
    public OrganismDataBuilder(final String propertyKeyPrefix, final Properties props)
            throws BuilderException {

        super(propertyKeyPrefix, props);

        addPropertyKey(PROP_URL);
        addPropertyKey(PROP_LINKAGE_GROUPS);
    }

    /**
     * Method description
     *
     *
     * @param newProperty
     */
    public void setPropertyURL(final String newProperty) {
        this.PROP_URL = newProperty;
    }

    /**
     * Method description
     *
     *
     * @param newProperty
     */
    public void setPropertyLinkageGroups(final String newProperty) {
        this.PROP_LINKAGE_GROUPS = newProperty;
    }

    /**
     * Method description
     *
     *
     * @return
     *
     * @throws BuilderException
     */
    @Override
    protected OrganismData buildFromProps() throws BuilderException {


        // VALIDATE PROP_URL
        final String url_prop_key = getFullPropertyKey(PROP_URL);

        try {
            ValidationUtil.validateProperty(url_prop_key, getProperty(url_prop_key));
        } catch (PropertyException ex) {
            throw new RuntimeException("Invalid property for OrganismDataBuilder", ex);
        }


        // VALIDATE PROP_URL
        final String linkage_groups_prop_key = getFullPropertyKey(PROP_LINKAGE_GROUPS);

        try {

            ValidationUtil.validateProperty(linkage_groups_prop_key,
                                            getProperty(linkage_groups_prop_key));

        } catch (PropertyException ex) {
            throw new RuntimeException("Invalid property for OrganismDataBuilder", ex);
        }


        // get input data
        try {


            // 1. get the provider
            final String url = getProperty(url_prop_key);
            final OrganismDataProvider provider = ProviderManager.getProvider(url,
                                                      OrganismDataProvider.class);


            // 2. get the files dir
            final String[] parts = url.split("//");

            if (parts.length != 2) {
                throw new RuntimeException("Invalid URL: " + url);
            }

            if (!new File(parts[1]).exists()) {

                throw new RuntimeException("Invalid URL: " + url + ". " + "Directory " + parts[1]
                                           + " does not exist.");
            }

            final File filesDir = new File(url.split("//")[1]);


            // 3. get the linkage groups
            final Set<Integer> linkageGroups = new LinkedHashSet<>();

            for (final String value : getProperty(linkage_groups_prop_key).split(",")) {

                try {
                    linkageGroups.add(Integer.valueOf(value));
                } catch (NumberFormatException ex) {
                    throw new RuntimeException("Invalid linkage group value " + value, ex);
                }
            }


            // 4. init the provider
            provider.setDataDirectory(filesDir);


            // check the linkage groups against the available ones
            // currently, off
            provider.setLinkageGroups(linkageGroups);
            setProvider(provider);

            data = provider.create();


            // 5. create the instance
            return data;
        } catch (ProviderException ex) {
            throw new RuntimeException("Error getiing organism data", ex);
        }
    }

    @Override
    public String getBuilderString() {
        return "NotYetImplemented";
    }

    /**
     * Method description
     *
     *
     * @return
     */
    private OrganismData getData() {

        if (data == null) {
            data = build();
        }

        return data;
    }

    /**
     * Linkage groups of the genome available for analysis.
     *
     * @return an unmodifiable set of linkage groups available for analysis
     */
    public Set<Integer> getLinkageGroups() {
        return getData().getLinkageGroups();
    }
}
